/**
 * 使用 librs232 实现的 transceiver
 * 张勇 41204@qq.com
 */

#include <stdio.h>
#include <limits.h>

#include "librs232/rs232.h"
#include "utils.hpp"
#include "trx_uart.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "TRXUART"
#include "log4app.h"

static enum rs232_baud_e rs232_baud(int baud) {
	switch(baud) {
	case 300   : return RS232_BAUD_300   ;
	case 2400  : return RS232_BAUD_2400  ;
	case 4800  : return RS232_BAUD_4800  ;
	case 9600  : return RS232_BAUD_9600  ;
	case 19200 : return RS232_BAUD_19200 ;
	case 38400 : return RS232_BAUD_38400 ;
	case 57600 : return RS232_BAUD_57600 ;
	case 115200: return RS232_BAUD_115200;
	case 460800: return RS232_BAUD_460800;
	case 921600: return RS232_BAUD_921600;
	default    : return RS232_BAUD_MAX   ;
	}
}

static enum rs232_data_e rs232_databits(int databits) {
	switch(databits) {
	case 5 : return RS232_DATA_5  ;
	case 6 : return RS232_DATA_6  ;
	case 7 : return RS232_DATA_7  ;
	case 8 : return RS232_DATA_8  ;
	default: return RS232_DATA_MAX;
	}
}

static enum rs232_parity_e rs232_parity(char parity) {
	switch(parity) {
	case 'n': return RS232_PARITY_NONE;
	case 'o': return RS232_PARITY_ODD;
	case 'e': return RS232_PARITY_EVEN;
	default : return RS232_PARITY_MAX ;
	}
}

static enum rs232_stop_e rs232_stopbits(int stopbits) {
	switch(stopbits) {
	case 1 : return RS232_STOP_1;
	case 2 : return RS232_STOP_2;
	default: return RS232_STOP_MAX ;
	}
}	

trx_uart* trx_uart::open(const char *addr, size_t mtu, regexp_match_t regexp_match_func) {
	CREATE_INSTANCE(trx_uart, addr, mtu, regexp_match_func);
}

transceiver_t* trx_uart_open(const char *addr, size_t mtu, regexp_match_t regexp_match_func) {
	trx_uart* trx = trx_uart::open(addr, mtu, regexp_match_func);
	return trx ? trx->trx() : NULL;
}

trx_uart::trx_uart(const char *addr, size_t mtu, regexp_match_t regexp_match_func): transceiver(regexp_match_func) {
	if(!addr || !mtu) { throw STD_ERR_INVALID_ARG; }
	int ret = 0;

	try {
		// 解析地址参数
		int port = -1, baudrate = 115200, databits = 8, stopbits = 1;
		char parity = 'n';
		ret = sscanf(addr, "uart://COM%d,%d,%d,%c,%d", &port, &baudrate, &databits, &parity, &stopbits);
		if(ret < 5 || port < 0) { loge("Invalid addr: %s !", addr); throw STD_ERR_INVALID_ARG; }
		enum rs232_baud_e enum_baud = rs232_baud(baudrate);
		enum rs232_data_e enum_databits = rs232_databits(databits);
		enum rs232_parity_e enum_parity = rs232_parity(parity);
		enum rs232_stop_e enum_stopbits = rs232_stopbits(stopbits);
		if(enum_baud >= RS232_BAUD_MAX || enum_databits >= RS232_DATA_MAX || enum_parity >= RS232_PARITY_MAX || enum_stopbits >= RS232_STOP_MAX) { loge("Invalid parameters: %s !", addr); throw STD_ERR_INVALID_ARG; }

		_rs232 = rs232_init();
		if(!_rs232) { loge("rs232_init() failed !"); throw STD_ERR_NO_MEM; }

		char buf[32];
		snprintf(buf, sizeof(buf) - 1, "COM%d", port);
		rs232_set_device(_rs232, buf);
		ret = rs232_open(_rs232);
		if(ret != RS232_ERR_NOERROR) { loge("rs232_open() failed: %d !", ret); throw STD_ERR_INVALID_ARG; }

		ret = rs232_set_baud(_rs232, enum_baud);     if(ret != RS232_ERR_NOERROR) { loge("rs232_set_baud() failed: %d !", ret);   throw STD_ERR_INVALID_ARG; }
		ret = rs232_set_data(_rs232, enum_databits); if(ret != RS232_ERR_NOERROR) { loge("rs232_set_data() failed: %d !", ret);   throw STD_ERR_INVALID_ARG; }
		ret = rs232_set_parity(_rs232, enum_parity); if(ret != RS232_ERR_NOERROR) { loge("rs232_set_parity() failed: %d !", ret); throw STD_ERR_INVALID_ARG; }
		ret = rs232_set_stop(_rs232, enum_stopbits); if(ret != RS232_ERR_NOERROR) { loge("rs232_set_stop() failed: %d !", ret);   throw STD_ERR_INVALID_ARG; }
		_mtu = mtu;
	} catch(std_err_t err) {
		destroy();
		throw err;
	}
}

void trx_uart::destroy() {
	if(_async_thread) {
		listen(false);
		//unios_thread_destroy(&_async_thread);
	}
	if(_rs232) {
		rs232_close(_rs232);
		free(_rs232->pt);
		free(_rs232); _rs232 = NULL;
	}
}

void trx_uart::txclear() {
	if(!_rs232) return;
	rs232_flush(_rs232);
}

void trx_uart::rxclear() {
	if(!_rs232) return;
	rs232_flush(_rs232);
}

std_err_t trx_uart::rxwait(long timeout_ms) {
	unsigned int ret = rs232_rxwait(_rs232, (unsigned int)timeout_ms);
	if(ret == RS232_ERR_TIMEOUT) { return STD_ERR_TIMEOUT; }
	if(ret != RS232_ERR_NOERROR) { loge("rs232_rxwait() failed: %d !", ret); return STD_ERR_FAIL; }
	return STD_ERR_OK;
}

std_err_t trx_uart::rxread(void* buf, size_t size, size_t *plen/*out*/) {
	if(!_rs232) return STD_ERR_INVALID_STATE;
	if(!buf || !size) return STD_ERR_INVALID_ARG;
	if(plen) *plen = 0;
    std_err_t err = STD_ERR_OK;
	unsigned int read_len = 0;
	int ret;
    _sync_reading = true;

    do {
	    // 先看设备接收队列中有多少数据
	    unsigned int in_queue = 0;
	    ret = rs232_in_queue(_rs232, &in_queue);
	    if(ret != RS232_ERR_NOERROR) { loge("rs232_in_queue() failed: %d !", ret); err = STD_ERR_FAIL; break; }

	    // 取 要读取的长度 和 设备接收队列中数据长度 二者较小的做为本次要读取的长度，以免 rs232_read() 阻塞
	    size = min(size, in_queue);

	    if(size) {
		    // 非阻塞读取数据
		    unsigned int read_len2 = 0;
		    ret = rs232_read(_rs232, (unsigned char*)buf, (unsigned int)size, &read_len2);
		    if(ret != RS232_ERR_NOERROR) { loge("rs232_read() failed: %d !", ret); err = STD_ERR_FAIL; break; }
		    read_len += read_len2;
	    }
    } while(0);

	if(plen) *plen = read_len;
    _sync_reading = false;
	return err;
}

std_err_t trx_uart::txwrite(const void* buf, uint64_t size, size_t *plen/*out*/) {
	if(!_rs232) return STD_ERR_INVALID_STATE;
	if(!buf || !size) return STD_ERR_INVALID_ARG;
	unsigned int write_len = 0;
	int ret = rs232_write(_rs232, (const unsigned char*)buf, (unsigned int)size, &write_len);
	if(ret != RS232_ERR_NOERROR) { loge("rs232_write() failed: %d !", ret); return STD_ERR_FAIL; }
	if(plen) *plen = write_len;
	return STD_ERR_OK;
}

void trx_uart::async_thread_func(void *cookie) {
	if(!cookie) return;
	trx_uart *pthis = (trx_uart*)cookie;
	unsigned int read_len = 0;
	unsigned int ret = 0;
	unsigned char buf[64];
	logd("trx_uart::async_thread running ...\r\n");

	while(!pthis->_async_thread_exit_flag) {
        // 如果应用层正在进行阻塞式读取，则本线程要暂停监听
        if(pthis->_sync_reading || pthis->iswait4ack()) { unios_ms_delay(1); continue; }

		// 阻塞式读取数据
		ret = rs232_read_timeout_forced(pthis->_rs232, buf, sizeof(buf), &read_len, 5);
		if(ret == RS232_ERR_PORT_CLOSED) break;
		if(ret == RS232_ERR_TIMEOUT) continue;
		if(ret != RS232_ERR_NOERROR) { loge("rs232_read() failed: %d !", ret); continue; }
		if(!read_len) continue;

		// 调用接收事件回调函数
		if(pthis->trx()->on_rx_data) {
			pthis->trx()->on_rx_data(pthis->trx()->on_rx_data_cookie, pthis->trx(), NULL, 0, 0, buf, read_len);
		}
	}

	pthis->_async_thread = NULL;
	logd("trx_uart::async_thread stopped.\r\n");
}

std_err_t trx_uart::listen(bool enable) {
	if(!enable) {
		if(!_async_thread) return STD_ERR_OK;
		// 设置异步监听线程退出标志
		_async_thread_exit_flag = true;
		// 等待线程退出
		while(_async_thread) unios_ms_delay(1);
		return STD_ERR_OK;
	} else {
		if(_async_thread) return STD_ERR_OK;
		// 设置异步监听线程退出标志
		_async_thread_exit_flag = false;
		// 启动异步处理线程
		return unios_thread_create(async_thread_func, this, UNIOS_THREAD_PRIORITY_HIGHEST, 0, 0, &_async_thread);
	}
}
